package websocket

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"errors"
	"github.com/lxzan/websocket/internal"
	"io"
	"net/http"
	"strings"
	"syscall"
)

// state
// 1 => 处理握手
// 2 => 读取帧头的前2字节
// 3 => 读取帧头的消息长度
// 4 => 读取帧头的掩码
// 5 => 读取消息内容

const rb_size = 4096

func NewConn(fd int, eventHandler Event, option *ServerOption) *Conn {
	return &Conn{
		fd:           fd,
		option:       option,
		eventHandler: eventHandler,
		rb:           make([]byte, rb_size),
		lb:           new(LinkedBuffer),
		wq:           newWorkerQueue(1),
		state:        1,
	}
}

type Conn struct {
	SessionStorage  SessionStorage
	compressEnabled bool

	fd           int // 文件描述符
	option       *ServerOption
	eventHandler Event
	fh           frameHeader
	state        uint8         // 消息解析状态
	rb           []byte        // 读数据用的临时buffer
	lb           *LinkedBuffer // 链式buffer
	wq           *workerQueue  // 写队列
	payloadSize  int
}

//func (c *Conn) isTextValid(opcode Opcode, payload []byte) bool {
//	if !c.config.CheckUtf8Enabled {
//		return true
//	}
//	switch opcode {
//	case OpcodeText, OpcodeCloseConnection:
//		return utf8.Valid(payload)
//	default:
//		return true
//	}
//}

func (c *Conn) emitMessage(err error) {

}

func (c *Conn) read(p []byte) (int, error) {
	num, err := syscall.Read(c.fd, p)
	if err != nil && err != io.EOF {
		return num, err
	}
	return num, nil
}

func (c *Conn) readMessage() error {
	for {
		num, err := c.read(c.rb)
		if err != nil {
			return err
		}
		_, _ = c.lb.Write(c.rb[:num])
		if num < bucketSize {
			break
		}
	}

	for {
		switch c.state {
		case 1:
			req, finished, err := c.readState1()
			if err != nil {
				return err
			}
			if !finished {
				return nil
			}
			if err := c.upgrade(req); err != nil {
				return err
			}
			c.state = 2
		case 2:
			if !c.lb.Gte(2) {
				return nil
			}
			_ = c.readState2()
			c.state = 3
		case 3:
			num, finished, err := c.readState3()
			if err != nil {
				return err
			}
			if !finished {
				return nil
			}
			c.state = 4
			c.payloadSize = num
		case 4:
			finished, err := c.readState4()
			if err != nil {
				return err
			}
			if !finished {
				return nil
			}
			c.state = 5
		default:
			msg, finished, err := c.readPayload()
			if err != nil {
				return err
			}
			if !finished {
				return nil
			}
			c.eventHandler.OnMessage(c, msg)
			c.state = 2
		}
	}
}

func (c *Conn) readState1() (*http.Request, bool, error) {
	b, ok := c.lb.readHeader()
	if !ok {
		return nil, false, nil
	}
	br := bufio.NewReader(bytes.NewReader(b))
	req, err := http.ReadRequest(br)
	return req, true, err
}

func (c *Conn) upgrade(r *http.Request) error {
	var session = new(sliceMap)
	var header = c.option.ResponseHeader.Clone()
	if !c.option.Authorize(r, session) {
		return internal.ErrUnauthorized
	}

	var compressEnabled = false
	if r.Method != http.MethodGet {
		return internal.ErrGetMethodRequired
	}
	if !internal.HttpHeaderEqual(r.Header.Get(internal.SecWebSocketVersion.Key), internal.SecWebSocketVersion.Val) {
		msg := "websocket version not supported"
		return errors.New(msg)
	}
	if !internal.HttpHeaderEqual(r.Header.Get(internal.Connection.Key), internal.Connection.Val) {
		return internal.ErrHandshake
	}
	if !internal.HttpHeaderEqual(r.Header.Get(internal.Upgrade.Key), internal.Upgrade.Val) {
		return internal.ErrHandshake
	}
	if val := r.Header.Get(internal.SecWebSocketExtensions.Key); strings.Contains(val, "permessage-deflate") && c.option.CompressEnabled {
		header.Set(internal.SecWebSocketExtensions.Key, internal.SecWebSocketExtensions.Val)
		compressEnabled = true
	}
	var websocketKey = r.Header.Get(internal.SecWebSocketKey.Key)
	if websocketKey == "" {
		return internal.ErrHandshake
	}

	if err := c.connectHandshake(r, header, websocketKey); err != nil {
		return err
	}
	//if err := netConn.SetDeadline(time.Time{}); err != nil {
	//	return err
	//}
	//if err := setNoDelay(netConn); err != nil {
	//	return err
	//}
	//return serveWebSocket(true, c.option.getConfig(), session, netConn, br, c.eventHandler, compressEnabled), nil

	c.SessionStorage = session
	c.compressEnabled = compressEnabled
	return nil
}

func (c *Conn) connectHandshake(r *http.Request, responseHeader http.Header, websocketKey string) error {
	if r.Header.Get(internal.SecWebSocketProtocol.Key) != "" {
		var subprotocolsUsed = ""
		var arr = internal.Split(r.Header.Get(internal.SecWebSocketProtocol.Key), ",")
		for _, item := range arr {
			if internal.InCollection(item, c.option.Subprotocols) {
				subprotocolsUsed = item
				break
			}
		}
		if subprotocolsUsed != "" {
			responseHeader.Set(internal.SecWebSocketProtocol.Key, subprotocolsUsed)
		}
	}

	var buf = make([]byte, 0, 256)
	buf = append(buf, "HTTP/1.1 101 Switching Protocols\r\n"...)
	buf = append(buf, "Upgrade: websocket\r\n"...)
	buf = append(buf, "Connection: Upgrade\r\n"...)
	buf = append(buf, "Sec-WebSocket-Accept: "...)
	buf = append(buf, internal.ComputeAcceptKey(websocketKey)...)
	buf = append(buf, "\r\n"...)
	for k, _ := range responseHeader {
		buf = append(buf, k...)
		buf = append(buf, ": "...)
		buf = append(buf, responseHeader.Get(k)...)
		buf = append(buf, "\r\n"...)
	}
	buf = append(buf, "\r\n"...)
	num, err := syscall.Write(c.fd, buf)
	return internal.CheckIOError(len(buf), num, err)
}

func (c *Conn) readState2() error {
	return internal.ReadN(c.lb, c.fh[0:2], 2)
}

func (c *Conn) readState3() (int, bool, error) {
	var payloadLength = 0
	var lengthCode = c.fh.GetLengthCode()
	switch lengthCode {
	case 126:
		if !c.lb.Gte(2) {
			return 0, false, nil
		}
		_, _ = c.lb.Read(c.fh[2:4])
		payloadLength = int(binary.BigEndian.Uint16(c.fh[2:4]))

	case 127:
		if !c.lb.Gte(8) {
			return 0, false, nil
		}
		_, _ = c.lb.Read(c.fh[2:10])
		payloadLength = int(binary.BigEndian.Uint64(c.fh[2:10]))
	default:
		payloadLength = int(lengthCode)
	}
	return payloadLength, true, nil
}

func (c *Conn) readState4() (bool, error) {
	flag := c.fh.GetMask()
	if !flag {
		return true, nil
	}
	if !c.lb.Gte(4) {
		return false, nil
	}
	_, _ = c.lb.Read(c.fh[10:14])
	return true, nil
}

func (c *Conn) readPayload() (*Message, bool, error) {
	if c.fh.GetOpcode() == OpcodeCloseConnection {
		return nil, false, internal.ErrConnClosed
	}

	if !c.lb.Gte(c.payloadSize) {
		return nil, false, nil
	}

	//c.lb.ZeroCopy()
	var buffer = c.lb.ZeroCopy(c.payloadSize)
	if buffer == nil {
		var payload = bufferpool.Get().Bytes()
		payload = payload[:c.payloadSize]
		_, _ = c.lb.Read(payload)
		buffer = bytes.NewBuffer(payload)
	}

	if c.fh.GetMask() {
		internal.MaskXOR(buffer.Bytes(), c.fh[10:14])
	}
	return &Message{Opcode: OpcodeText, Data: buffer}, true, nil
}

func (c *Conn) Write(opcode Opcode, payload []byte) error {
	// 不要删除 opcode == OpcodeText
	//if opcode == OpcodeText && !c.isTextValid(opcode, payload) {
	//	return internal.NewError(internal.CloseUnsupportedData, internal.ErrTextEncoding)
	//}

	//if c.compressEnabled && opcode.isDataFrame() && len(payload) >= c.config.CompressThreshold {
	//	return c.compressAndWrite(opcode, payload)
	//}

	var n = len(payload)
	//if n > c.config.WriteMaxPayloadSize {
	//	return internal.CloseMessageTooLarge
	//}

	var header = frameHeader{}
	headerLength, _ := header.GenerateHeader(true, true, false, opcode, n)
	//var totalSize = n + headerLength
	var buf = bufferpool.Get()
	buf.Write(header[:headerLength])
	buf.Write(payload)
	var contents = buf.Bytes()
	//if !c.isServer {
	//	internal.MaskXOR(contents[headerLength:], maskBytes)
	//}
	//var err = internal.WriteN(c.conn, contents, totalSize)
	//myBufferPool.Put(buf, buf.Cap())
	_, err := syscall.Write(c.fd, contents)
	return err
}

func (c *Conn) WriteAsync(opcode Opcode, payload []byte) {
	c.wq.Push(func() {
		c.Write(opcode, payload)
	})
}
